package org.pushforward.iwillsurvive.servlets;

import java.io.IOException;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.LinkedHashSet;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.pushforward.iwillsurvive.data.DatabaseManager;
import org.pushforward.iwillsurvive.data.Post;
import org.pushforward.iwillsurvive.data.TypeValuePair;
import org.pushforward.iwillsurvive.data.TypeValuePair.PairTypeEnum;
import org.pushforward.iwillsurvive.helpers.Utils;

/**
 * Servlet implementation class PostManager
 */
@WebServlet(description = "A servlet responsible for the general use of posts", urlPatterns = { "/PostManager" })
public class PostManager extends HttpServlet 
{
	private static final long serialVersionUID = 1L;
	private static boolean updatePosts = false;
	
	private final DatabaseManager db = DatabaseManager.getInstance();
       
    /**
     * @see HttpServlet#HttpServlet()
     */
    public PostManager() 
    {
        super();
    }

	private LinkedHashSet<Post> GetPostsByEmail(String email) throws SQLException
	{
		Utils.Log("Getting posts for " + ((email == null) ? "Everyone" : email));
		LinkedHashSet<Post> posts = new LinkedHashSet<Post>();
		String[] getPostsStatement = {"select * from posts where email like ?;"};
		TypeValuePair[][] getPostsPairs = {{new TypeValuePair(PairTypeEnum.STRING, 
																(email == null) ? "%" : email)}};
		StringBuilder getPostsResult = new StringBuilder();
		
		if (!DatabaseManager.getInstance().ExecuteStatements(getPostsStatement, getPostsPairs, getPostsResult))
		{
			throw new SQLException("Error getting posts.");
		}
		
		String[] resultSet = getPostsResult.toString().split("\n");
		
		for (int index = 0; index < resultSet.length; index++)
		{
			String resultRow = resultSet[index];
			String[] resultFields = resultRow.split(",");
			
			posts.add(new Post(resultFields, false));
		}
		
		return posts;
	}
	
    private String GetPost(int id)
    {
		try
		{
			Post post = new Post(id);
			Utils.Log("Getting post " + Integer.toString(id));
			return post.toString();
		} 
		catch (SQLException e)
		{
			System.err.println(e.getMessage());
			e.printStackTrace();
			return "error";
		}
    }
    
    /**
     * Will get the posts associated with the given user email or all of them when there is none.
     * @param email
     * @return
     */
    private String GetAllPosts(String email)
    {
		try
		{
			LinkedHashSet<Post> posts = GetPostsByEmail(email);
			Iterator<Post> iter = posts.iterator();
			StringBuilder responseBuilder = new StringBuilder();
			
			while (iter.hasNext())
			{
				Post post = iter.next();
				responseBuilder.append(post.toString());
			}

			Utils.Log("Getting all posts" + ((email == null) ? (".") : (" of " + email)));
			responseBuilder.setLength(responseBuilder.length() - 1);
			return responseBuilder.toString().replaceAll("null", "");
		} catch (SQLException e)
		{
			System.err.println(e.getMessage());
			e.printStackTrace();
			return "error";
		}
    }

    private String GetPostsByUser(String email)
    {
    	Utils.Log("Getting all posts where users contain: " + email);
    	String[] getPostsStatement = {"select * from posts where users like '%" + email + "%';"};
    	StringBuilder getPostsResult = new StringBuilder();
    	
    	if (!db.ExecuteStatements(getPostsStatement, db.GetEmptyParameters(1), getPostsResult))
    	{
    		return "error";
    	}
    	
    	getPostsResult.setLength(getPostsResult.length() - 1);
		return getPostsResult.toString();
    }
    
	/**
	 * handles a leave team request
	 * @param request
	 * @return
	 */
 	private boolean LeaveTeam(HttpServletRequest request)
	{
		try
		{
			String email = request.getParameter("email");
			Post post = new Post(Integer.parseInt(request.getParameter("id")));
			post.RemoveUser(email);
			Utils.Log("User " + email + " is leaving team " + Integer.toString(post.Id()));
		}
		catch (NumberFormatException | SQLException e)
		{
			e.printStackTrace();
			return false;
		}
		updatePosts = true;
		return true;
	}

	/**
	 * handles a join team request
	 * @param request
	 * @return
	 */
	private boolean JoinTeam(HttpServletRequest request)
	{
		try
		{
			String email = request.getParameter("email");
			Post post = new Post(Integer.parseInt(request.getParameter("id")));
			post.AddUser(email);
			Utils.Log("User " + email + " is joining team " + Integer.toString(post.Id()));
		}
		catch (NumberFormatException | SQLException e)
		{
			e.printStackTrace();
			return false;
		}
		updatePosts = true;
		return true;
	}

	/**
	 * Rewrites the geodesics table
	 * @param geodesics the geodesics data
	 * @return true if operation succeeded
	 */
	private boolean UpdateGeodesics(String geodesics)
	{
		Utils.Log("Rebuilding Geodesics Table.");
		String[] geodesicsStrings = geodesics.split("@");
		String[] clearGeodesicsStatement = {"truncate table geodesics"};
		String[] geodesicsInsertStatement = new String[1];
		TypeValuePair[][] geodesicsInsertPairs = new TypeValuePair[1][4];
		
		db.ExecuteStatements(clearGeodesicsStatement, db.GetEmptyParameters(1));
		
		for (int index = 0; index < geodesicsStrings.length; index++)
		{
			String[] geodesicIDs = geodesicsStrings[index].split(",");
			geodesicsInsertPairs[0][0] = new TypeValuePair(PairTypeEnum.INT, new Integer(geodesicIDs[0]));
			geodesicsInsertPairs[0][1] = new TypeValuePair(PairTypeEnum.STRING, geodesicIDs[1]);
			geodesicsInsertPairs[0][2] = new TypeValuePair(PairTypeEnum.INT, new Integer(geodesicIDs[2]));
			geodesicsInsertPairs[0][3] = new TypeValuePair(PairTypeEnum.STRING, geodesicIDs[3]);
			geodesicsInsertStatement[0] = new String("insert into geodesics values (?, ?, ?, ?);");
			db.ExecuteStatements(geodesicsInsertStatement, geodesicsInsertPairs);
		}

		return true;
	}

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	@Override
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 
	{
		String responseText = null;
		String email = request.getParameter("email");
		
		switch (request.getParameter("action"))
		{
			case "getnextid":
				responseText = Integer.toString(db.NextID());
				Utils.Log("NextID Requested: " + responseText);
				break;
			case "getpost":
				responseText = GetPost(Integer.parseInt(request.getParameter("id")));
				break;
			case "getallposts":
				responseText = GetAllPosts(email);
				break;
			case "getpostsbyuser":
				responseText = GetPostsByUser(email);
				break;
			case "needupdate":
				responseText = "";
				if (!updatePosts)
				{
					response.setStatus(500);
				}
				else
				{
					Utils.Log("Update Needed.");
					updatePosts = false;
				}
				break;
			default:
				responseText = "error";
		}
		
		response.getWriter().write(responseText);
	}
	
	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	@Override
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException 
	{
		switch (request.getParameter("action"))
		{
			case "jointeam":
				if (!JoinTeam(request))
				{
					response.setStatus(500);
				}
				break;
			case "leaveteam":
				if (!LeaveTeam(request))
				{
					response.setStatus(500);
				}
				break;
			case "updategeodesics":
				if (!UpdateGeodesics(request.getParameter("geodesics")))
				{
					response.setStatus(500);
				}
				break;
			case "requireupdate":
				updatePosts = true;
				break;
			default:
				response.setStatus(500);
		}
	}

}
